package com.ruoyi.web.controller.system;

import java.io.IOException;
import java.security.*;
import java.sql.SQLOutput;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.SM2;
import com.antherd.smcrypto.sm2.Sm2;
import com.google.common.util.concurrent.RateLimiter;
import com.ruoyi.common.exception.user.AntiReplayException;
import com.ruoyi.common.utils.simpSM2.SimpSM2Util;
import com.ruoyi.system.domain.vo.PubUidVO;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.system.service.ISysMenuService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 登录验证
 *
 * @author ruoyi
 */
@Slf4j
@RestController
public class SysLoginController
{

    /**
     * 限流策略 ：1秒钟2个请求
     */
    private final RateLimiter limiter = RateLimiter.create(0.5);

    private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");


    @Autowired
    private SysLoginService loginService;

    @Autowired
    private ISysMenuService menuService;

    @Autowired
    private SysPermissionService permissionService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${MaxLoginCount.param}")
    private Integer maxCount;


    @PostMapping("/test")
    public String lotest(){
        boolean tryAcquire = limiter.tryAcquire(0, TimeUnit.SECONDS);

        if (!tryAcquire) {
            log.warn("进入服务降级，时间{}", LocalDateTime.now().format(dtf));
            return "进入服务降级，时间{}";
        }else {
            return "111";
        }
    }

    /**
     * 登录方法
     *
     * @param loginBody 登录信息
     * @return 结果
     */
    @PostMapping("/login")
    public AjaxResult login(@RequestBody LoginBody loginBody, HttpServletResponse response,HttpServletRequest request) throws IOException {
        AjaxResult ajax = AjaxResult.success();


        boolean tryAcquire = limiter.tryAcquire(0, TimeUnit.SECONDS);

        if (!tryAcquire) {
            log.warn("进入服务降级，时间{}", LocalDateTime.now().format(dtf));
            throw new AntiReplayException();
        }


        if("1".equals(loginBody.getStatus())){
            System.out.println("当前登录为 使用url登录");
            //String priKey = MessageUtils.message("user.password.my");
            String password =loginBody.getPassword(); //SimpSM2Util.decrypt(priKey, loginBody.getPassword());
            System.out.println("解密后的密码是这个。。。" + password);
            String token = loginService.login(loginBody.getUsername(), password, loginBody.getCode(),
                    loginBody.getUuid());
            ajax.put(Constants.TOKEN, token);
            return ajax;
        }
        // 生成令牌
        //System.out.println("解析前" + loginBody.getPassword());
        //String priKey = MessageUtils.message("user.password.my");
//        String priKey = (String) redisTemplate.opsForValue().get("private");
        //System.out.println("存储的prikey 拿出来" + priKey);
        //String username = SimpSM2Util.decrypt("", loginBody.getUsername());
//        String password = SimpSM2Util.decrypt(priKey, loginBody.getPassword());
        //System.out.println("解析后" + password);
        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
                loginBody.getUuid());
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @GetMapping("getInfo")
    public AjaxResult getInfo()
    {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        user.setPassword("");
        user.setEmail("");
        user.setPhonenumber("");
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(user);
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", user);
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        return ajax;
    }

    /**
     * getPublicKey
     *
     * @return getPublicKey
     */
    @GetMapping("getPublicKey")
    public PubUidVO getPublicKey() throws NoSuchAlgorithmException {

        SM2 sm2 = SmUtil.sm2();
        String pub = HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false)).toUpperCase();
        String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey())).toUpperCase();

        log.info("Hpub=={}",pub);
        log.info("HprivateKey=={}",privateKey);

        //System.out.println("前端发送的公约  "+publicKey);
        redisTemplate.opsForValue().set("public",pub);
        redisTemplate.opsForValue().set("private",privateKey);

        //String publicKey = (String) redisTemplate.opsForValue().get("qdPublic");


        //生成uid   用前端的公钥加密后发送到前端
        //生成当前时间
        long timestamp_server = System.currentTimeMillis();


        PubUidVO pubUidVO = new PubUidVO();
        pubUidVO.setPubKey(pub);

        pubUidVO.setTimestamps(timestamp_server);
        //第一次请求，发送给前端后端的公钥 和当钱服务器的时间
        return pubUidVO;
    }

    /**
     * getPublicKey
     *
     * @return getPublicKey
     */
    @GetMapping("insertPublicKey")
    public PubUidVO insertPublicKey( String publicKey) throws NoSuchAlgorithmException {
        //用后端公钥加密前端公钥  应该用后端私钥解密

        //前端机密的公钥
        String priKey = (String) redisTemplate.opsForValue().get("private");
        //System.out.println("存储的prikey 拿出来" + priKey);
        //
        //String username = SimpSM2Util.decrypt("", loginBody.getUsername());
        String zz = SimpSM2Util.decrypt(priKey, publicKey);

        redisTemplate.opsForValue().set("qdPublic",zz);
        log.info("Qpub=={}",zz);

        String s = UUID.randomUUID().toString();
        log.info("uid=={}",s);
        redisTemplate.opsForValue().set("uid",s);
        //对s加密
        //后端加密，用前端的公钥
        String result = Sm2.doEncrypt(s,zz);
        PubUidVO pubUidVO = new PubUidVO();
        pubUidVO.setUid(result);
        return pubUidVO;
    }

    /**
     * getPublicKey
     *
     * @return getPublicKey
     */
    @GetMapping("/OnlyGetPublicKey")
    public String OnlyGetPublicKey() throws NoSuchAlgorithmException {

        SM2 sm2 = SmUtil.sm2();
        String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey()));
        String publicKey = HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false));




        return (String)redisTemplate.opsForValue().get("public");
    }

    /**q
     * 获取路由信息
     *
     * @return 路由信息
     */
    @GetMapping("getRouters")
    public AjaxResult getRouters()
    {
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
        return AjaxResult.success(menuService.buildMenus(menus));
    }



    public  boolean CheckLoginCount(String username){

        // 1.判断当前用户是否已经登录
        if(redisTemplate.hasKey(username)){
            //已经登录 判断个数
            Integer loginCount = (Integer) redisTemplate.opsForValue().get(username);
            if(loginCount == null){
                System.out.println("没有人使用1");
                return true;
            }
            System.out.println("当前的已经登录的个数是" + loginCount);
            if(loginCount > 0){
                System.out.println("应该返回false");
                return false;
            }
            return true;
        }else {
            System.out.println("没有人使用");
            return true;
        }
    }

    //多一个登录
    public void LoginUser(String username){
//        if(redisTemplate.hasKey(username)){
//            Integer loginCount = (Integer) redisTemplate.opsForValue().get(username);
//            System.out.println("原先是" + loginCount +"个");
//            redisTemplate.opsForValue().set(username,loginCount+1,30, TimeUnit.MINUTES);
//        }else {
//            redisTemplate.opsForValue().set(username,1,30, TimeUnit.MINUTES);
//        }
        redisTemplate.opsForValue().set(username,1,30, TimeUnit.MINUTES);
    }
}
